/* Tabela de Simbolos implementada com um Array de Elementos. 
   O seu tamanho maximo eh definido pela constante TAMPILHA.
*/



#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "tabSimbolosArray.h"


//TabelaSimbolos tab_simbolos;

/* Funcao fazPilhaVazia( TabelaSimbolos* tab_simbolos )
   *  Acao:        Cria uma pilha vazia.
   *  Entrada:     nenhuma
   *  Saida:       nenhuma
   *  Suposicoes:  nenhuma
   *  Algoritmo:   Cria uma pilha vazia, com o topo apontando para -1.
   */

void fazPilhaVazia( TabelaSimbolos* tab_simbolos ) {
	tab_simbolos->topo = -1;
}

/* Funcao vazia( TabelaSimbolos* tab_simbolos )
   *  Acao:        Informa se uma pilha estah vazia.
   *  Entrada:     nenhuma
   *  Saida:       inteiro com o valor 1 ou 0.
   *  Suposicoes:  nenhuma
   *  Algoritmo:   Retorna 1 se a pilha estiver vazia e 0 em caso contrário.
   */

int vazia( TabelaSimbolos* tab_simbolos ) {
	if ( tab_simbolos->topo == -1 ) 
		return 1;
	return 0;
}

/* Funcao empilha( ElemTS elem_TS, TabelaSimbolos* tab_simbolos )
   *  Acao:        Empilha um elemento.
   *  Entrada:     um elemento do tipo EmenTS.
   *  Saida:       nenhuma
   *  Suposicoes:  nenhuma
   *  Algoritmo:   Incrementa o topo e empilha o elemento passado como parametro
   *			   no topo da pilha.
   *			   Se a pilha estiver cheia retorna uma mensagem de erro e sai
   *			   do programa.
   */

void empilha( ElemTS elem_TS, TabelaSimbolos* tab_simbolos ) {
	if ( tab_simbolos->topo > ( TAMPILHA - 1 ) ) {
		printf("\nErro: a Tabela de Simbolos esta cheia.\n\n");
		exit(1);
	}
	else {
		tab_simbolos->topo++;
		tab_simbolos->elem[tab_simbolos->topo] = elem_TS;
	}
}


/* Funcao desempilha( TabelaSimbolos* tab_simbolos )
   *  Acao:        Desempilha um elemento.
   *  Entrada:     nenhuma
   *  Saida:       item: ElemTS.
   *  Suposicoes:  nenhuma
   *  Algoritmo:   Salva o elemento do topo na variavel item, decrementa o topo
   *			   e retorna o elemento desempilhado do topo da pilha.
   *			   Se a pilha estiver vazia retorna uma mensagem de erro e sai
   *			   do programa.
   */

ElemTS desempilha( TabelaSimbolos* tab_simbolos ) {
	ElemTS item;
	if ( vazia( tab_simbolos ) ) {
		printf("\nErro: a Tabela de Simbolos esta' vazia.\n\n");
		exit(1);
	}
	else {
		item = tab_simbolos->elem[tab_simbolos->topo]; 
		tab_simbolos->topo--;
		return(item);
	}
}


/* Funcao desempilha_elementos( int qtd, TabelaSimbolos* tab_simbolos )
   *  Acao:        Desempilha n elementos.
   *  Entrada:     qtd: int
   *  Saida:       nenhuma
   *  Suposicoes:  nenhuma
   *  Algoritmo:   Decrementa o topo em n posicoes conforme indicado pelo pa-
   *			   rametro qtd, removendo as n ultimas entradas.
   *			   Se a pilha estiver vazia retorna uma mensagem de erro e sai
   *			   do programa.
   *			   Se a pilha estiver com elementos mas a quantidade a ser de-
   *			   sempilhada for maior do que a da pilha, também retornara' 
   *			   uma mensagem de erro e sai do programa.
   */

void desempilha_elementos( int qtd, TabelaSimbolos* tab_simbolos) {
	if ( vazia( tab_simbolos ) ) {
		printf("\nErro: a Tabela de Simbolos esta' vazia.\n\n");
		exit(1);
	}
	else {
		if ( qtd >  ( tab_simbolos->topo + 1 ) ) {
			printf("\nErro: a quantidade a ser desempilhada eh maior do que a da Tabela de Simbolos!\n\n");
			exit(1);
		}
		else 
			tab_simbolos->topo = tab_simbolos->topo - qtd;
	}
}


/* Funcao busca_elemento_nivel( char *id, int nivel, TabelaSimbolos* tab_simbolos )
   *  Acao:        Busca a ultima ocorrencia de um elemento no nivel lexico atual.
   *  Entrada:     identificador: char*, nivel: int.
   *  Saida:       res: int, com o valor maior ou igual a -1 .
   *  Suposicoes:  o nivel lexico deve ser o ultimo inserido na tabela.
   *  Algoritmo:   Busca a ultima ocorrencia de um identificador na Tabela de 
   *			   Simbolos do mesmo nivel lexico que o informado como parametro.
   *			   Retorna a posicao(um inteiro maior que zero) do identificador 
   *			   se este foi encontrado, ou retorna -1 em caso contrario.
   */

int busca_elemento_nivel( char *id, int nivel, TabelaSimbolos* tab_simbolos ) {

	int i, res = -1;
	
	i = tab_simbolos->topo;
	while ( ( i >= 0 ) && ( nivel == tab_simbolos->elem[i].nivel ) &&
			( ( res = strcmp( id, tab_simbolos->elem[i].ident ) )  != 0 )  ) {
		i--;
    };

	if ( res == 0 )
		return i;
   	return -1;  // nao achou o identificador
}

/* Funcao busca_elemento( char *id, TabelaSimbolos* tab_simbolos )
   *  Acao:        Busca a ultima ocorrencia de um elemento em qualquer nivel lexico.
   *  Entrada:     uma string cotendo um identificador
   *  Saida:       um inteiro com o valor maior ou igual a -1 .
   *  Suposicoes:  nenhuma.
   *  Algoritmo:   Busca a ultima ocorrencia de um identificador na Tabela de 
   *			   Simbolos do mesmo nivel lexico que o informado como parametro.
   *			   Retorna a posicao(um inteiro maior que zero) do identificador 
   *			   se este foi encontrado, ou retorna -1 em caso contrario.
   */

int busca_elemento( char *id, TabelaSimbolos* tab_simbolos ) {
         
	int i = tab_simbolos->topo; int res = -1;

	while ( ( i >= 0 ) && 
			( ( res = strcmp( id, tab_simbolos->elem[i].ident ) )  != 0 )  ) {
		i--;
    };
         
	if ( res == 0 )
		return i;
   	return -1;  // nao achou o identificador
}



/* Funcao imprime_tabela_de_simbolos( TabelaSimbolos* tab_simbolos )
   *  Acao:        Imprime toda a tabela de simbolos.
   *  Entrada:     nenhuma
   *  Saida:       nenhuma
   *  Suposicoes:  nenhuma
   *  Algoritmo:   Imprime toda a tabela de simbolos na saida stderr.
   */

void imprime_tabela_de_simbolos( TabelaSimbolos* tab_simbolos ) {
	int i;
	
	if ( vazia( tab_simbolos ) ) {
		fprintf( stderr, "\nNao ha elementos na Tabela de Simbolos.\n\n" );
		return;
	}
	
	fprintf(stderr,"\n\nTabela de Simbolos:\n\n");
	fprintf(stderr,"ITEM \tIDENT \tCATEG \tTIPO \t\tEND. LEXICO \tPASSAG. PARAM. \tNIVEL LEXICO \tROT. INTERNO \tNUM. PF \tTIPO PF\n");
	
	for( i=0; i <= tab_simbolos->topo; i++ ) {
		
		switch( tab_simbolos->elem[i].categ ) {
			case categ_vs: 
				fprintf(stderr, "%2d \t%s \t%d \t%7s \t%5s \t\t\t \t%d\n", i, tab_simbolos->elem[i].ident, 
						tab_simbolos->elem[i].categ, tab_simbolos->elem[i].tipo, 
						tab_simbolos->elem[i].ender_lexico, tab_simbolos->elem[i].nivel );
				break;
			case categ_pf:
				fprintf(stderr, "%2d \t%s \t%d \t%7s \t%5s \t\t%d \t\t%d", i, tab_simbolos->elem[i].ident, 
						tab_simbolos->elem[i].categ, tab_simbolos->elem[i].tipo, 
						tab_simbolos->elem[i].ender_lexico, tab_simbolos->elem[i].u_categ.pf.passagem_parametros, 
						tab_simbolos->elem[i].nivel );
				imprime_indice_params( i, tab_simbolos );
				fprintf(stderr, "\n");
				break;
			case categ_proc: 
				fprintf(stderr, "%2d \t%s \t%d \t%7s \t%5s \t\t\t\t%d \t\t%s \t\t%d", i, tab_simbolos->elem[i].ident, 
						tab_simbolos->elem[i].categ, tab_simbolos->elem[i].tipo, 
						tab_simbolos->elem[i].ender_lexico, tab_simbolos->elem[i].nivel, 
						tab_simbolos->elem[i].u_categ.proc.rot_interno, tab_simbolos->elem[i].u_categ.proc.numPF );
				imprime_indice_params( i, tab_simbolos );
				fprintf(stderr, "\n");
				break;		
			case categ_func:
				fprintf(stderr, "%2d \t%s \t%d \t%7s \t%5s \t\t\t\t%d \t\t%s \t\t%d", i, tab_simbolos->elem[i].ident, 
						tab_simbolos->elem[i].categ, tab_simbolos->elem[i].tipo, 
						tab_simbolos->elem[i].ender_lexico, tab_simbolos->elem[i].nivel, 
						tab_simbolos->elem[i].u_categ.func.rot_interno, tab_simbolos->elem[i].u_categ.func.numPF );
				imprime_indice_params( i, tab_simbolos );
				fprintf(stderr, "\n");
				break;
			case categ_label:
				fprintf(stderr, "%2d \t%s \t%d \t%7s \t%5s \t\t\t\t%d \t\t%s\n", i, tab_simbolos->elem[i].ident, 
						tab_simbolos->elem[i].categ, tab_simbolos->elem[i].tipo, tab_simbolos->elem[i].ender_lexico,
						tab_simbolos->elem[i].nivel, tab_simbolos->elem[i].u_categ.lab.rot_interno);
				break;			
		}
	}
} 


/* Funcao busca_elemento_qualquer_nivel( char *id, int nivel, TabelaSimbolos* tab_simbolos )
   *  Acao:        Busca um identificador em qualquer nivel lexico.
   *  Entrada:     identificador: char*, nivel: int.
   *  Saida:       res: int, com o valor maior ou igual a -1 .
   *  Suposicoes:  o nivel lexico deve ser o ultimo inserido na tabela.
   *  Algoritmo:   Busca um identificador na Tabela de Simbolos com o mesmo nivel lexico 
   *			   que o informado como parametro.
   *			   Retorna a posicao(um inteiro maior que zero) do identificador 
   *			   se este foi encontrado, ou retorna -1 em caso contrario.
   */

int busca_elemento_qualquer_nivel( char *id, int nivel, TabelaSimbolos* tab_simbolos ) {
         
	int i = tab_simbolos->topo; int res = -1;

	while ( i >= 0 ) {
		if ( ( nivel == tab_simbolos->elem[i].nivel ) &&
			( ( res = strcmp( id, tab_simbolos->elem[i].ident ) )  == 0 )  ) {
			break;   // encontrou identificador com o nível informado
		}
		else {
			i--;
			continue;
		}			
    };
         
	if ( res == 0 )
		return i;
   	return -1;  // nao achou o identificador
}

/* Funcao busca_numero_varslocal_ao_rotulo( char* rotulo, TabelaSimbolos* tab_simbolos )
   *  Acao:        Verifica se houve declaracao de variaveis locais num determinado Rotulo interno
   *  Entrada:     rotulo, tabSimbolos
   *  Saida:       inteiro i indicando o nr de vars locais declaradas.
   *  Suposicoes:  as vars locais visiveis sempre pertenceram a um procedimento ou funcao ativa
   *		   na tabela de simbolos.
   *  Algoritmo:   Percorre a partir do Rotulo interno encontrado até o topo da pilha
		   procurando vars locais, se encontrar soma em cont.
   */
int busca_numero_varslocal_ao_rotulo( char* rotulo, TabelaSimbolos* tab_simbolos ) {

	int i = tab_simbolos->topo;
	int cont = 0;
	
	while ( i >= 0 ) {
		
		if ( ( tab_simbolos->elem[i].categ == categ_proc ) || ( tab_simbolos->elem[i].categ == categ_func ) ) {
		
			if ( !strcmp(rotulo, tab_simbolos->elem[i].u_categ.proc.rot_interno ) ) {
				i++;
				while (i <= tab_simbolos->topo ){
					if ( tab_simbolos->elem[i].categ == categ_vs ) {
						cont++;
					}
					i++;
				}
				return cont;
				//return tab_simbolos->elem[i].u_categ.proc.numVarsLocal;
			}
								
			else if ( !strcmp(rotulo, tab_simbolos->elem[i].u_categ.func.rot_interno ) ) {
				i++;
				while (i <= tab_simbolos->topo ){
					if ( tab_simbolos->elem[i].categ == categ_vs ) {
						cont++;
					}
					i++;
				}				return cont;
				//return tab_simbolos->elem[i].u_categ.func.numVarsLocal;
			}
		}							
		i--;
	}
	return -1;
}

int busca_numero_varslocal_no_nivel( int nivel, TabelaSimbolos* tab_simbolos ) {

	int i = tab_simbolos->topo;
	int cont = 0;

	while ( i >= 0 ) {
		if ( nivel == tab_simbolos->elem[i].nivel ){
			if ( tab_simbolos->elem[i].categ == categ_vs ){
				cont = cont + 1;
			}
		}
		i--;
	}
	return cont;
}



/* Funcao imprime_indice_params( int i, TabelaSimbolos* tab_simbolos )
   *  Acao:        Imprime os elementos de um vetor.
   *  Entrada:     nenhuma
   *  Saida:       nenhuma
   *  Suposicoes:  nenhuma
   *  Algoritmo:   Imprime os elementos contidos no vetor de parametros na saida stderr.
   */
void imprime_indice_params( int i, TabelaSimbolos* tab_simbolos ) {
	int j;
	fprintf( stderr, "\t\t" );
	for ( j = 0; j < tab_simbolos->elem[i].u_categ.proc.numPF; j++ ) {
		fprintf( stderr, "%d ", tab_simbolos->elem[i].u_categ.proc.parametros[j] );
	}
}

/* Funcao desempilha_elementos_nivel_acima( int nivel, TabelaSimbolos* tab_simbolos )
   *  Acao:        Desempilha todos os elementos que estão num nivel lexico acima.
   *  Entrada:     nivel: int
   *  Saida:       nenhuma
   *  Suposicoes:  nenhuma
   *  Algoritmo:   Conta o numero de elementos que estão no nivel lexico superior ao 
   *			   informado como parametro e em seguida desempilha da TS a quantidade
   *			   de elementos que foi contada.
   */

void desempilha_elementos_nivel_acima( int nivel, TabelaSimbolos* tab_simbolos) {
	int qtd = 0, i;
	if ( vazia( tab_simbolos ) ) {
		printf("\nErro: a Tabela de Simbolos estah vazia.\n\n");
		exit(1);
	}
	else {
		for ( i = tab_simbolos->topo; tab_simbolos->elem[i].nivel > nivel; i-- ) {
			qtd++;		
		}		
		if ( qtd >  ( tab_simbolos->topo + 1)  ) {
			printf("\nErro: a quantidade a ser desempilhada eh maior do que a da Tabela de Simbolos!\n\n");
			exit(1);
		}
		else 
			tab_simbolos->topo = tab_simbolos->topo - qtd;
	}
}


/* Funcao desempilha_elementos_coletor( int num, int nivel, TabelaSimbolos* tab_simbolos )
   *  Acao:        Desempilha uma quantidade elementos e apos isso verifica se ficou
   *			   algum PF ou var. simples na TS e os desempilha.
   *  Entrada:     nivel: int, num:int.
   *  Saida:       nenhuma
   *  Suposicoes:  nenhuma
   *  Algoritmo:   Desempilha a qtd de elementos passados por parametro, e apos isso procura
   *			   PF ou var. simples que tenha ficado na TS, no mesmo nível que o informado
   *			   como parametro, e em seguida os desempilha.
   */

void desempilha_elementos_coletor( int num, int nivel, TabelaSimbolos* tab_simbolos) {
	int i;
	
	desempilha_elementos( num, tab_simbolos );		
	
	for ( i = tab_simbolos->topo; tab_simbolos->elem[i].nivel == nivel; i-- ) {
		if ( tab_simbolos->elem[i].categ == categ_pf || tab_simbolos->elem[i].categ == categ_vs || 
tab_simbolos->elem[i].categ == categ_label ) {
			desempilha( tab_simbolos );
		}
	}
}


/* Programa para testes. Deve ser removido depois que a tabela de simbolos estiver pronta. */
/*int main() {
	TabelaSimbolos tabSimbolos;
	TabelaSimbolos *ts;
	
	ElemTS item0, item1, item2, item3, item4;
	int pos = -1;

	ts = &tabSimbolos;
	
	fazPilhaVazia( ts );
	
	strcpy( item0.ident, "a" );
	item0.categ = categ_vs;
	strcpy( item0.tipo, "integer" );
	strcpy( item0.ender_lexico, "0,0" );
	item0.nivel = 0;
	
	empilha( item0, ts );

	strcpy( item1.ident, "b" );
	item1.categ = categ_pf;
	strcpy( item1.tipo, "integer" );
	strcpy( item1.ender_lexico, "0,1" );
	item1.nivel = 0;
	item1.u_categ.pf.passagem_parametros = por_valor;

	empilha( item1, ts );
	
	strcpy( item2.ident, "p" );
	item2.categ = categ_proc;
	strcpy( item2.tipo, "proc" );
	strcpy( item2.ender_lexico, "1,-4" );
	item2.nivel = 1;
	strcpy( item2.u_categ.proc.rot_interno, "R01" );
	item2.u_categ.proc.numPF = 0;

	empilha( item2, ts );
	
	strcpy( item3.ident, "q" );
	item3.categ = categ_func;
	strcpy( item3.tipo, "integer" );
	strcpy( item3.ender_lexico, "2,-4" );
	item3.nivel = 2;
	strcpy( item3.u_categ.func.rot_interno, "R01" );
	item3.u_categ.func.numPF = 2;

	empilha( item3, ts );
	
	strcpy( item4.ident, "a" );
	item4.categ = categ_func;
	strcpy( item4.tipo, "integer" );
	strcpy( item4.ender_lexico, "2,-4" );
	item4.nivel = 2;
	strcpy( item4.u_categ.func.rot_interno, "R01" );
	item4.u_categ.func.numPF = 2;
	
	empilha( item4, ts );

	//desempilha_elementos( 1, ts );
	
	imprime_tabela_de_simbolos( ts );
	
	pos = busca_elemento_qualquer_nivel( "a", 0, ts );
	
	if ( pos == -1 ) 
		printf( "\nElemento nao encontrado! pos = %d\n\n", pos );
	else
		printf( "\nElemento encontrado na posicao %d.\n\n", pos );
}*/
